import type { Dto<%=argv.resourceNameCapitalize%>Create, Dto<%=argv.resourceNameCapitalize%>SelectRes, Dto<%=argv.resourceNameCapitalize%>Update, Entity<%=argv.resourceNameCapitalize%> } from 'vona-module-<%=argv.moduleInfo.relativeName%>';

import assert from 'node:assert';
import { describe, it } from 'node:test';
import { appMetadata } from 'vona';
import { app } from 'vona-mock';
import { SymbolOpenApiOptions } from 'vona-module-a-openapiutils';

describe('<%=argv.resourceName%>.test.ts', { concurrency: false }, () => {
  it('action:<%=argv.resourceName%>:openapiMetadata', async () => {
    await app.bean.executor.mockCtx(async () => {
      const controllerBeanFullName = '<%=argv.moduleInfo.relativeName%>.controller.<%=argv.resourceName%>';
      const controller = app.bean.onion.controller
        .getOnionsEnabledCached()
        .find(item => item.beanOptions.beanFullName === controllerBeanFullName)
        ?.beanOptions.beanClass;
      if (!controller) throw new Error(`${controllerBeanFullName} not found`);

      const controllerMetadata = appMetadata.getMetadata<any>(
        SymbolOpenApiOptions,
        controller,
      );
      assert.equal(
        controllerMetadata?.summary?.toString(),
        '<%=argv.moduleInfo.relativeName%>::<%=argv.resourceNameCapitalize%>Controller',
      );

      const moduleApiPath = app.util.combineApiPath('<%=argv.moduleActionPathRaw%>', '', true, true);
      const expectedSummaries = {
        create: ['post', moduleApiPath, 'Create <%=argv.resourceNameCapitalize%>'],
        select: ['get', moduleApiPath, 'Query <%=argv.resourceNameCapitalize%> List'],
        view: ['get', `${moduleApiPath}/{id}`, 'View <%=argv.resourceNameCapitalize%>'],
        update: ['patch', `${moduleApiPath}/{id}`, 'Update <%=argv.resourceNameCapitalize%>'],
        delete: ['delete', `${moduleApiPath}/{id}`, 'Delete <%=argv.resourceNameCapitalize%>'],
        deleteBulk: ['post', `${moduleApiPath}/bulk/delete`, 'Bulk Delete <%=argv.resourceNameCapitalize%>'],
      } as const;
      for (const [action, [method, path, summary]] of Object.entries(expectedSummaries)) {
        const apiJson = await app.bean.openapi.generateJsonOfControllerAction(
          controller,
          action,
          'V31',
        );
        const operation = (apiJson.paths as any)?.[path]?.[method];
        assert.ok(operation, `${action} operation is missing`);
        assert.equal(operation.summary?.toJSON(), summary);
      }
    });
  });

  it('action:<%=argv.resourceName%>:rbacCatalog', async () => {
    await app.bean.executor.mockCtx(async () => {
      const controllerBeanFullName = '<%=argv.moduleInfo.relativeName%>.controller.<%=argv.resourceName%>';
      const catalog = app.bean.rbacCatalog.getCatalog();
      const expectedInherits = {
        create: undefined,
        select: undefined,
        view: undefined,
        update: undefined,
        delete: undefined,
        deleteBulk: `${controllerBeanFullName}#delete`,
      } as const;
      for (const [action, actionInheritKey] of Object.entries(expectedInherits)) {
        const descriptor = catalog.get(`${controllerBeanFullName}#${action}`);
        assert.ok(descriptor, `${action} RBAC action is missing`);
        assert.equal(descriptor.actionInheritKey, actionInheritKey);
        assert.equal(descriptor.options.dataScope, undefined);
      }
    });
  });

  it('action:<%=argv.resourceName%>', async () => {
    await app.bean.executor.mockCtx(async () => {
      const data: Dto<%=argv.resourceNameCapitalize%>Create = {
        name: '__Tom__',
        description: 'This is a test',
      };
      const dataUpdate: Dto<%=argv.resourceNameCapitalize%>Update = {
        name: '__TomNew__',
        description: 'This is a test',
      };
      let <%=argv.resourceName%>Id: Entity<%=argv.resourceNameCapitalize%>['id'] | undefined;
      // sign in as an unrestricted test identity
      await app.bean.passport.signinMock();
      try {
        // create
        <%=argv.resourceName%>Id = await app.bean.executor.performAction('post', '<%=argv.moduleActionPathRaw%>', { body: data });
        assert.equal(!!<%=argv.resourceName%>Id, true);
        // findMany
        const selectRes: Dto<%=argv.resourceNameCapitalize%>SelectRes = await app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>');
        assert.equal(selectRes.list.findIndex(item => item.name === data.name) > -1, true);
        // update
        const updateRes = await app.bean.executor.performAction('patch', '<%=argv.moduleActionPathRaw%>/:id', {
          params: { id: <%=argv.resourceName%>Id },
          body: dataUpdate,
        });
        assert.equal(updateRes, null);
        // findOne
        let <%=argv.resourceName%>: Entity<%=argv.resourceNameCapitalize%> = await app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>/:id', { params: { id: <%=argv.resourceName%>Id } });
        assert.equal(<%=argv.resourceName%>.name, dataUpdate.name);
        // delete
        const deleteRes = await app.bean.executor.performAction('delete', '<%=argv.moduleActionPathRaw%>/:id', { params: { id: <%=argv.resourceName%>.id } });
        assert.equal(deleteRes, null);
        // findOne
        <%=argv.resourceName%> = await app.bean.executor.performAction('get', '<%=argv.moduleActionPathRaw%>/:id', { params: { id: <%=argv.resourceName%>.id } });
        assert.equal(<%=argv.resourceName%>, undefined);
      } finally {
        if (<%=argv.resourceName%>Id) {
          await app.scope('<%=argv.moduleInfo.relativeName%>').model.<%=argv.resourceName%>.deleteById(
            <%=argv.resourceName%>Id,
            { disableDeleted: true },
          );
        }
        await app.bean.passport.signout();
      }
    });
  });
});
